package ua.org.tumakha.resume.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;

/**
 * @author Yuriy Tumakha
 */
public class XMLValidator {

    private static final Log log = LogFactory.getLog(XMLValidator.class);

    public static boolean parse(File xmlFile) throws FileNotFoundException, IOException, ParserConfigurationException,
            SAXException {
        String filename = xmlFile.getName();
        boolean result = false;
        try {
            StringBuffer errorBuff = new StringBuffer();
            InputSource input = new InputSource(new FileInputStream(xmlFile));
            // Set systemID so parser can find the dtd with a relative URL in
            // the source document.
            String systemBase = "file:/" + xmlFile.getAbsolutePath().replace('\\', '/');
            input.setSystemId(systemBase);
            SAXParserFactory spfact = SAXParserFactory.newInstance();

            spfact.setValidating(true);
            spfact.setNamespaceAware(true);

            SAXParser parser = spfact.newSAXParser();
            XMLReader reader = parser.getXMLReader();

            // Instantiate inner-class error and lexical handler.
            Handler handler = new Handler(filename, errorBuff);
            reader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
            parser.parse(input, handler);

            if (handler.containsDTD && !handler.errorOrWarning) // valid
            {
                log.info("VALID file: " + filename);
                result = true;
            } else if (handler.containsDTD) // not valid
            {
                log.error("NOT VALID file: " + filename + "\n");
                log.error(errorBuff.toString());
            } else // no DOCTYPE to use for validation
            {
                log.error("NO DOCTYPE DECLARATION " + filename + "\n");
            }
        } catch (Exception e) // Serious problem!
        {
            log.error("NOT WELL-FORMED " + filename + ".\n\t" + e.getMessage() + "\n");
            e.printStackTrace();
        }
        return result;
    }

    // Catch any errors or warnings, and verify presence of doctype statement.
    static class Handler extends DefaultHandler implements LexicalHandler {
        boolean errorOrWarning;
        boolean containsDTD;
        String sourceFile;
        StringBuffer errorBuff;

        Handler(String sourceFile, StringBuffer errorBuff) {
            super();
            this.sourceFile = sourceFile;
            this.errorBuff = errorBuff;
            errorOrWarning = false;
            containsDTD = false;
        }

        public void error(SAXParseException exc) {
            errorBuff.append(sourceFile + "\n\tError: " + exc.getMessage() + "\n");
            errorOrWarning = true;
        }

        public void warning(SAXParseException exc) {
            errorBuff.append(sourceFile + "\n\tWarning:" + exc.getMessage() + "\n");
            errorOrWarning = true;
        }

        // LexicalHandler methods; all no-op except startDTD().

        // Set containsDTD to true when startDTD event occurs.
        public void startDTD(String name, String publicId, String systemId) throws SAXException {
            containsDTD = true;
        }

        public void endDTD() throws SAXException {
        }

        public void startEntity(String name) throws SAXException {
        }

        public void endEntity(String name) throws SAXException {
        }

        public void startCDATA() throws SAXException {
        }

        public void endCDATA() throws SAXException {
        }

        public void comment(char ch[], int start, int length) throws SAXException {
        }
    }
}
